//
//  BookmarkManager.swift
//  YoruNeko
//
//  Created by  mahoPunch on 2021/6/28.
//

import Foundation

class BookmarkManager {
    //数据读写工具对象
    private static let io = YRNKDataStore()
    private static let encoder = JSONEncoder()
    private static let decoder = JSONDecoder()
    //当前所缓存的书签所属用户名
    private static var cachedUserUuid = UserManager.currUserUuid
    //书签记录头节点，启动时从存储读取，应用关闭时写入到
    private static var cachedBookmarkRoot: YRNKBookmark = YRNKBookmark.init()
    //urlSet 用来标识url是否存在于yrnkbookmark树中，并存储该url对应的parent的UUID
    private static var urlMapUuid:[String:UUID] = [:]
    
    private static var folderMap: [UUID:YRNKBookmark] = [cachedBookmarkRoot.this.id:cachedBookmarkRoot]
//    private static var cachedInterval = 300.0
    static func getCachedBookmarkRoot()->YRNKBookmark{
        return cachedBookmarkRoot
    }
    
    //保存内存中的历史记录到文件中，切换用户时、退出应用时调用。
    static func saveBookmarkList() throws -> Bool  {
        encoder.outputFormatting = .prettyPrinted
        let bookmarkJsonData = try encoder.encode(cachedBookmarkRoot)
        let json = try JSON.init(data: bookmarkJsonData)
        return io.writeToStorage(user_uuid: cachedUserUuid, srcType: .bookmark, jsonObj: json)
    }
    
    //根据 user UUID 获取历史列表
    static func readBookmarkList(of uuid: UUID) throws -> YRNKBookmark {
        //如果历史记录缓存列表为空, 或者要读取的用户与上次读取的用户不同（切换用户后） 开始从存储中读取
        if cachedBookmarkRoot.children?.isEmpty ?? true || !(uuid==cachedUserUuid) {
            if let json = io.readFromStorage(user_uuid: uuid, srcType: .bookmark) {
                cachedBookmarkRoot = try decoder.decode(YRNKBookmark.self, from: json.rawData())
            }
        }
        setParentAttributeHelper(root: cachedBookmarkRoot)
        print("bookmarkScene: Readed list")
        print(cachedBookmarkRoot.this.name)
        return cachedBookmarkRoot
    }
    
    
    //递归设置parent的值
    private static func setParentAttributeHelper(root: YRNKBookmark){
        guard let childrenList = root.children else{
            return
        }
        for child in childrenList {
            child.parent = root
            setParentAttributeHelper(root: child)
            if child.isDirectory {
                folderMap.updateValue(child, forKey: child.this.id )
            }
        }
    }
    
    //插入成功返回true 失败返回false
    static func addBookmark(parentUuid: UUID, childPoint: YRNKBookmark) -> Bool {
        print("bookmarkScene: added bookmarkItem")
        //对于非文件夹节点处理
        if !childPoint.isDirectory{
            var urlContained: Bool = false
            if let url = childPoint.this.url{
                urlContained = urlMapUuid.contains(where: {$0.key == url})
            }
            if !urlContained{
                let findPoint = findItemByUuidHelper(uuid: parentUuid, root: cachedBookmarkRoot)
                if let parentPoint = findPoint{
                    parentPoint.addChild(child: childPoint)
                    if let url = childPoint.this.url{
                        urlMapUuid.updateValue(childPoint.this.id, forKey: url)
                    }
                    return true
                }else{
                    return false
                }
            }
        }
        //对于文件夹节点处理
        else{
            let findPoint = findItemByUuidHelper(uuid: parentUuid, root: cachedBookmarkRoot)
            if let parentPoint = findPoint{
                parentPoint.addChild(child: childPoint)
                folderMap[childPoint.this.id] = childPoint
                return true
            }else{
                return false
            }
        }
        return false
    }

    static func addBookmarkList(parentUuid: UUID, childPointList: [YRNKBookmark]) -> Bool{
        print("bookmarkScene: added bookmarkList")
        var result = true
        for childPoint in childPointList {
            if !addBookmark(parentUuid: parentUuid, childPoint: childPoint){
                result = false
            }
        }
        return result
    }
    
  
    static func deleteBookmarkList(uuidList: [UUID]) -> Bool{
        var result = true
        for uuid in uuidList {
            if !deleteBookmark(uuid: uuid){
                result = false
            }
        }
        return result
    }
        
    //成功删除数据返回true 数据不存在或删除失败返回false
    static func deleteBookmark(uuid: UUID) -> Bool{
        if let point = findItemByUuid(uuid: uuid) {
            if let pParent = point.parent {
                pParent.children?.removeAll(where: {$0.this.id == uuid})
                if point.isDirectory {
                    folderMap.removeValue(forKey: uuid)
                    deleteUrlMapUuidHelper(root: point)
                }else{
                    if let key = urlMapUuid.first(where: {$0.value == uuid})?.key{
                        urlMapUuid.removeValue(forKey: key)
                    }
                }
                return true
            }
        }
        return false
    }
    
    static func deleteUrlMapUuidHelper(root: YRNKBookmark) -> Bool{
        guard let children = root.children else {
            return false
        }
        var result = true
        for child in children {
            if child.isDirectory {
                if !deleteUrlMapUuidHelper(root: child) {
                    result = false
                }
            }else{
                if let key = urlMapUuid.first(where: {$0.value == child.this.id})?.key{
                    urlMapUuid.removeValue(forKey: key)
                }
            }
        }
        return result
    }
    
    static func updateItemByUuid(uuid: UUID,bookmark: Bookmark)->Bool{
        guard let point = folderMap.first(where: {$0.key == uuid})?.value else{
            return false
        }
        point.this = bookmark
        return true
    }
    
    static func findItemByUuid(uuid:UUID) -> YRNKBookmark?{
        return findItemByUuidHelper(uuid: uuid, root: cachedBookmarkRoot)
    }
    
    static func findUuidByUrl(url:String) ->UUID?{
        return urlMapUuid[url]
    }
    
    static func isUrlExist(url:String) -> Bool{
        if urlMapUuid.contains(where: {$0.key == url}) {
            return true
        }
        return false
    }
    
    private static func findItemByUuidHelper(uuid: UUID,root: YRNKBookmark)->YRNKBookmark?{
        if root.this.id == uuid{
            return root
        }else{
            if let childList = root.children{
                for child in childList {
                    if let result = findItemByUuidHelper(uuid: uuid,root: child){
                        return result
                    }
                }
            }
        }
        return nil
    }
    
    static func deleteAll(){
        cachedBookmarkRoot = YRNKBookmark.init()
        urlMapUuid.removeAll()
        folderMap.removeAll()
        folderMap.updateValue(cachedBookmarkRoot, forKey: cachedBookmarkRoot.this.id)
    }
    
    static func refresh() {
        do {
            try saveBookmarkList()
            deleteAll()
            cachedUserUuid = UserManager.currUserUuid
            try cachedBookmarkRoot = readBookmarkList(of: cachedUserUuid)
        } catch {
            
        }
    }
    
    
//    static var data:Data?
//    static func testEncoding(){
//
//        let parent = YRNKBookmark(reference: Bookmark(date_added: Date(), last_visited: Date(), name: "baidu"), parent: nil, isDirectory: true)
//        let child = YRNKBookmark(reference: Bookmark(date_added: Date(), last_visited: Date(), name: "baidu2", url: "www.baidu.com"), parent: parent, isDirectory: false)
//        parent.addChild(child: child)
//        print(parent.children?.count)
//        let modelData = BookmarkModelData(with: [parent, child])
//        let encoder = JSONEncoder()
//        encoder.outputFormatting = .prettyPrinted
//
//        do {
//            data = try encoder.encode(modelData.list)
//            print(String(data: data!, encoding: .utf8))
//        } catch {
//
//        }
//    }
//
//    static func testdecoding(){
//        let decoder = JSONDecoder()
//        do {
//            let bookmark = try decoder.decode([YRNKBookmark].self, from: data!)
//            print(bookmark[0].this)
//            print(bookmark[1])
//        } catch {
//
//        }
//    }
}
